package cn.wlinker.driver.bacnet.utils;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.wlinker.driver.bacnet.constant.ActiveEnum;
import cn.wlinker.driver.bacnet.domain.BacnetBaseBean;
import cn.wlinker.driver.bacnet.domain.BacnetLocalDeviceBean;
import com.serotonin.bacnet4j.LocalDevice;
import com.serotonin.bacnet4j.RemoteDevice;
import com.serotonin.bacnet4j.exception.BACnetException;
import com.serotonin.bacnet4j.service.confirmed.WritePropertyRequest;
import com.serotonin.bacnet4j.type.Encodable;
import com.serotonin.bacnet4j.type.enumerated.ObjectType;
import com.serotonin.bacnet4j.type.enumerated.PropertyIdentifier;
import com.serotonin.bacnet4j.type.primitive.*;

import java.lang.Boolean;

/**
 * bacnet写入处理器
 *
 * @author gxsjx
 * @version 1.0
 * @date 2024/10/22
 * Copyright © wlinker.cn
 */
public class BacnetWriteHandler {

    /**
     * @param bacnetLocalDeviceBean
     * @param dto
     */
    public static Boolean write(BacnetLocalDeviceBean bacnetLocalDeviceBean, BacnetBaseBean dto) {
        LocalDevice localDevice = null;
        try {
            String remoteId = dto.getRemoteId();
            String objectId = dto.getObjectId();
            localDevice = BacnetLocalDeviceHelper.getLocalDevice(bacnetLocalDeviceBean);
            if (ObjectUtil.isNull(localDevice) || localDevice.getRemoteDevices().isEmpty()) {
                throw new RuntimeException("未找到远程设备，请检查请求地址！");
            }
            RemoteDevice remoteDevice = localDevice.getRemoteDevice(Convert.toInt(remoteId)).get();
            if (ObjectUtil.isNull(remoteDevice)) {
                throw new RuntimeException("网关设备不存在");
            }
            ObjectIdentifier objectIdentifier = ObjectIdentifierUtils.getWriteObjectIdentifierById(objectId);
            Primitive primitive = null;
            //值为空时为释放该点位的优先级
            String value = dto.getValue();
            Integer priority = dto.getPriority();
            if (CharSequenceUtil.isBlank(value)) {
                Null aNull = new Null();
                writePresentValue(localDevice, remoteDevice, objectIdentifier, aNull, priority);
                return false;
            }
            if (ObjectUtil.isNull(objectIdentifier)) {
                throw new RuntimeException("objectIdentifier 为空！");
            } else if (objectIdentifier.getObjectType().equals(ObjectType.analogOutput) || objectIdentifier.getObjectType().equals(ObjectType.analogValue)) {
                primitive = new Real(Convert.toFloat(value));
            } else if (objectIdentifier.getObjectType().equals(ObjectType.binaryOutput) || objectIdentifier.getObjectType().equals(ObjectType.binaryValue)) {
                primitive = ActiveEnum.getPrimitiveByValue(value);
            } else if (objectIdentifier.getObjectType().equals(ObjectType.multiStateOutput) || objectIdentifier.getObjectType().equals(ObjectType.multiStateValue)) {
                primitive = new UnsignedInteger(Convert.toInt(value));
            }
            if (ObjectUtil.isNull(primitive)) {
                throw new RuntimeException("value值格式化异常！");
            }
            writePresentValue(localDevice, remoteDevice, objectIdentifier, primitive, priority);
            return true;
        } catch (Exception e) {
            //解决异常发生时资源不释放造成的后续调用地址占用异常问题
            if (localDevice != null) {
                localDevice.terminate();
            }
            throw new RuntimeException(e);
        }
    }

    public static void writePresentValue(LocalDevice localDevice, RemoteDevice d, ObjectIdentifier oid, Encodable value, Integer priority) throws BACnetException {
        if (ObjectUtil.isNotEmpty(priority)) {
            if (priority < 1) {
                priority = 1;
            } else if (priority > 16) {
                priority = 16;
            }
        }
        writeProperty(localDevice, d, oid, PropertyIdentifier.presentValue, value, priority);
    }

    public static void writeProperty(LocalDevice localDevice, RemoteDevice d, ObjectIdentifier oid, PropertyIdentifier pid, Encodable value, Integer priority) throws BACnetException {
        UnsignedInteger unsignedInteger = null;
        if (ObjectUtil.isNotEmpty(priority)) {
            unsignedInteger = new UnsignedInteger(priority);
        }
        localDevice.send(d, new WritePropertyRequest(oid, pid, null, value, unsignedInteger)).get();
    }
}
